package com.songcan.oauth.other;


import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.songcan.common.vo.UserDetailsNew;
import com.songcan.model.business.YfkUser;
import com.songcan.oauth.handle.CoreSimpleUrlAuthenticationFailureHandler;
import com.songcan.oauth.service.YfkUserService;
import com.songcan.oauth.service.YfkUserWxInfoService;
import com.songcan.oauth.utils.AliyunMobileKeyUtil;
import com.songcan.oauth.utils.WeChatLoginUtil;
import com.songcan.oauth.utils.WeixinLoginUserVo;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import static com.songcan.common.contans.CoreContants.*;



public class CoreAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    private boolean postOnly = true;

    private CoreSimpleUrlAuthenticationFailureHandler coreSimpleUrlAuthenticationFailureHandler = new CoreSimpleUrlAuthenticationFailureHandler();
    private RedisTemplate redisTemplate;
    private boolean continueChainBeforeSuccessfulAuthentication = false;
    private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
    private String wxPublicAppId;
    private String wxPublicSecretKey;
    private YfkUserService yfkUserService;
    private YfkUserWxInfoService yfkUserWxInfoService;


    public CoreAuthenticationFilter(String wxPublicSecretKeyu,String wxPublicAppId,YfkUserWxInfoService yfkUserWxInfoService,YfkUserService yfkUserService) {
        super(new AntPathRequestMatcher(THRID_LOGIN, HttpMethod.POST.name()));
        this.wxPublicAppId = wxPublicAppId;
        this.wxPublicSecretKey = wxPublicSecretKeyu;
        this.yfkUserWxInfoService = yfkUserWxInfoService;
        this.yfkUserService = yfkUserService;
    }



    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;


        if (postOnly && !request.getMethod().equals(METHOD_POST)) {

            this.unsuccessfulAuthentication(request, response, new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod()));
            return;
        }

        if (!this.requiresAuthentication(request, response)) {
            chain.doFilter(request, response);
        } else {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Request is to process authentication");
            }

            Authentication authResult;
            try {
                authResult = this.attemptAuthentication(request, response);
                if (authResult == null) {
                    return;
                }

                this.sessionStrategy.onAuthentication(authResult, request, response);
            } catch (InternalAuthenticationServiceException var8) {
                this.logger.error("An internal error occurred while trying to authenticate the user.", var8);
                this.unsuccessfulAuthentication(request, response, var8);
                return;
            } catch (AuthenticationException var9) {
                this.unsuccessfulAuthentication(request, response, var9);
                return;
            }

            if (this.continueChainBeforeSuccessfulAuthentication) {
                chain.doFilter(request, response);
            }

            this.successfulAuthentication(request, response, chain, authResult);
        }

    }

    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        SecurityContextHolder.clearContext();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Authentication request failed: " + failed.toString(), failed);
            this.logger.debug("Updated SecurityContextHolder to contain null Authentication");
            this.logger.debug("Delegating to authentication failure handler " + this.coreSimpleUrlAuthenticationFailureHandler);
        }

        this.coreSimpleUrlAuthenticationFailureHandler.onAuthenticationFailure(request, response, failed);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {


        String type = request.getParameter("type");
        String mobile = request.getParameter("mobile");
        String weChatCode = request.getParameter("weChatCode");
        String mobileKeyToken = request.getParameter("mobileKeyToken");
        Boolean bindMobile = true;
        UserDetailsNew userDetailsNew = null;
        Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
        switch (type){
            case TYPE_MOBILE:{
                YfkUser yfkUser = yfkUserService.getOne(Wrappers.lambdaQuery(new YfkUser()).eq(YfkUser::getPhone,mobile)
                .eq(YfkUser::getDelFlag,0));
                if(Objects.nonNull(yfkUser)) {
                    mobile = mobile.trim();
                    grantedAuthorities.add(new SimpleGrantedAuthority(TYPE_MOBILE));
                    userDetailsNew =  new UserDetailsNew(mobile,grantedAuthorities,yfkUser.getEnabled()==1,yfkUser.getId(),yfkUser.getLocked()==0,yfkUser.getRealName());
                }
            }
            break;
            case TYPE_WECHAT:{
               //微信登录
                if(Objects.isNull(weChatCode)||StrUtil.isEmpty(weChatCode)){
                    throw new UsernameNotFoundException("微信code不能为空");
                }
                WeChatLoginUtil weChatLoginUtil = new WeChatLoginUtil(wxPublicAppId,wxPublicSecretKey,redisTemplate);
                WeixinLoginUserVo userVo = weChatLoginUtil.weixinLogin(weChatCode);
                if(Objects.isNull(userVo)){
                    throw new UsernameNotFoundException("微信登录异常");
                }

                Optional<YfkUser> yfkUsers = yfkUserService.selectByUnionId(userVo.getUnionid());
                if(yfkUsers.isPresent()) {
                    YfkUser yfkUser = yfkUsers.get();
                    bindMobile = userVo.getMobileBind();
                    mobile = userVo.getUnionid();
                    grantedAuthorities.add(new SimpleGrantedAuthority(TYPE_MOBILE));
                    userDetailsNew =  new UserDetailsNew(mobile,grantedAuthorities,yfkUser.getEnabled()==1,yfkUser.getId(),yfkUser.getLocked()==0,yfkUser.getRealName());

                }
            }
            break;
            case TYPE_ALIYUN:{
               String key = new AliyunMobileKeyUtil().login(mobileKeyToken);
               if(Objects.isNull(key)){
                   throw new UsernameNotFoundException("一鍵登錄，token錯誤");
               }
               YfkUser yfkUser = yfkUserService.getOne(Wrappers.lambdaQuery(new YfkUser()).eq(YfkUser::getPhone,key));
               if(Objects.isNull(yfkUser)){
                   yfkUser = new YfkUser(IdWorker.getId(),key);
                   yfkUserService.save(yfkUser);
               }
                grantedAuthorities.add(new SimpleGrantedAuthority(TYPE_MOBILE));
                userDetailsNew =  new UserDetailsNew(mobile,grantedAuthorities,yfkUser.getEnabled()==1,yfkUser.getId(),yfkUser.getLocked()==0,yfkUser.getRealName());
            }
            break;
        }


        CoreLoginAuthenticationToken mobileAuthenticationToken = new CoreLoginAuthenticationToken(type,bindMobile,userDetailsNew);
        setDetails(request, mobileAuthenticationToken);
        return this.getAuthenticationManager().authenticate(mobileAuthenticationToken);
    }

    private void setDetails(HttpServletRequest request,
                            AbstractAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }

    public void setRedisTemplate(RedisTemplate redisTemplate){
        this.redisTemplate = redisTemplate;
    }



}
